iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
0
Software Development

用30天介紹 open source 專案 Ohara 系列 第 25

Day 25 關於 Ohara 的測試程式 (一)

  • 分享至 

  • xImage
  •  

Ohara 在開發時很要求寫測試程式,主要的目的是因為 Ohara 是 Open Source 所以開發者的人數很多,所以不可能一個人就了解所有模組每一段程式的邏輯。因此就有可能不小心改到一段程式碼而影響到其它地方不能執行,最簡單的例子是修改了 Configurator Restful API 傳入參數的資料格式,如果前端沒有撰寫測試程式就會等到使用者在操作 UI 時才會發現程式無法運作,這時前端的開發者就會開始追蹤程式碼,等追到最後才發現 API 傳入的參數有修改。 這個問題如果一開始有撰寫測試可以很快的發現問題並解決,可以減少很多成本的花費,以下列出幾個寫測試程式的好處:

  • 減少一些低級的失誤,例如是程式邏輯寫錯,可以很快的發現問題

  • 程式在重構的時侯,修改起來比較有信心。如果沒有測試程式有很多地方都不敢改

  • 如果發現程式有 bug 可以再增加測試情境,調整程式的邏輯

  • 程式換其它人開發時,如果不了解程式的邏輯也可以先閱讀測試程式,並且可以使用測試程式在 IDE 上執行 Debug mode 來追蹤程式碼

以上就是撰寫測試程式可以得到的幾個好處,雖然寫測試程式有可能會影響到開發的進度,但是這是值得的投資。沒有寫測試程式當系統愈做愈大時所花費的成本會更多,而且程式裡面可能會隱藏許多沒被發現的 Bug,到最後程式可能就不太能修改,加一個功能可能會花費很長的時間。

以下就來簡單的介紹有關於 Ohara 測試程式的部份,Ohara 的測試分為單元測試以及整合測試。今天就來說明單元測試的部份:

單元測試主要用來測試程式的邏輯是否正確,以下舉一段 Ohara 單元測試程式的 sample:

  @Test 
  public void testTags() { 
    Row row = Row.of(Arrays.asList("tag", "tag2"), Cell.of("aa", "aa"), Cell.of("b", 123)); 
    Assert.assertEquals(2, row.tags().size()); 
    Assert.assertEquals("tag", row.tags().get(0)); 
    Assert.assertEquals("tag2", row.tags().get(1)); 
  } 

以上的測試程式主要用來測試 Row 的 tag list 的數量是否正確以及每一個元素內的值是否正確,單元測試主要測試的就是 output 出來的值是否有符合預期的結果,如果沒有符合預期的結果就代表程式邏輯要再去做確認和修改。

在寫測試程式時呼叫方法,傳入的參數如果要求是一個介面 (interface) 或是類別 (class),這時介面的實作或是類別有可能是其它開發者在做,有可能還沒做好或是要避免測試程式的耦合性,不用太關心實作的內容,這時可以使用 mock 的 library 或是建立 Fake 的 class,舉例如下:

Ohara 開發使用的 mock library 是 mockito,版本為 1.10.19,以下是 Ohara 測試程式使用 mock 的一段 sample

  @Test(expected = IllegalArgumentException.class) 
  public void emptyConfigInfos() { 
    ConfigInfos infos = Mockito.mock(ConfigInfos.class); 
    Mockito.when(infos.values()).thenReturn(Collections.emptyList()); 
    SettingInfo.of(infos); 
  } 

在這裡主要會去 mock ConfigInfos 類別,並且當呼叫到 infos 的 values 方法時,要求回傳值為空的 List,之後再把 infos 的變數傳給 SettingInfo.of 測試。測試程式預期當 SettingInfo.of 收到空的 list 時會收到 IllegalArgumentException 的 Exception

有關於 SettingInfo 的程式可以參考以下的連結:
https://github.com/oharastream/ohara/blob/master/ohara-kafka/src/main/java/com/island/ohara/kafka/connector/json/SettingInfo.java

以下是建立 Fake class 測試的 sample code:

  @Test 
  def testBkCreatorZKNotExists(): Unit = { 
    val node1Name = "node1" 
    val node1 = node(node1Name) 
    val brokerCollie = new FakeBrokerCollie(Seq(node1), Seq.empty, Seq.empty) 

    val bkCreator: Future[BrokerClusterInfo] = brokerCollie.creator 
      .clusterName("cluster123") 
      .imageName(BrokerApi.IMAGE_NAME_DEFAULT) 
      .zookeeperClusterName("zk123456") 
      .clientPort(9092) 
      .exporterPort(9093) 
      .jmxPort(9094) 
      .nodeName(node1Name) 
      .create() 

    an[NoSuchClusterException] should be thrownBy { 
      Await.result(bkCreator, TIMEOUT) 
    } 
  } 

以上的測試程式主要用來測試在建立 Broker cluster 時,如果沒有建立 zk123456 的 Zookeeper Cluster 會收到 NoSuchClusterException,在這裡 FakeBrokerCollie 會繼承 BrokerCollie,creator 的程式邏輯會寫到 BrokerCollie 的 trait ,這樣就可以使用 FakeBrokerCollie 去測試 creator 方法的邏輯。不用等到 K8SBrokerCollieImpl 或是 ssh 版本的 BrokerCollieImpl 實作完成,再去做寫測試的部份。 FakeBrokerCollie 的好處就是不用 Kubernetes 和 SSH 的環境也可以執行程式的邏輯測試。

有了 mock 以及建立 Fake class 的測試還是不夠的,因為在 Connector 的執行,是多執行緒在執行,所以有可能我們使用 mock 或是 Fake 的測試邏輯沒問題,但是在部署到真實的 Worker cluster 環境上執行確發現資料筆數,跟我們預期的不一樣。 因此 Ohara 提供在測試程式裡面執行 MiniCluster 的測試方式,確保 connector 程式的執行結果是正確的,以下是建立 MiniCluster 程式簡單的寫法範例:

    val brokerNumber = 1 
    val workerNumber = 1 
    val zookeeper = Zookeepers.local(0) 
    val brokers = Brokers.local(zookeeper, (1 to brokerNumber).map(x => 0).toArray) 
    val workers = Workers.local(brokers, (1 to workerNumber).map(x => 0).toArray) 

    val workerClient = WorkerClient(workers.connectionProps()) 

以上的寫法可以設定 Zookeeper、Broker 和 Worker cluster 的數量,如果數量設定太大,硬體 (CPU、記憶體) 不夠強的話有可能測試程式會發生 timeout exception。

啟動了 cluster 之後就可以把 worker 的連線資訊設定給 WorkerClient,這樣就可以在測試程式上執行 Connector 的測試了。

完整的 connector 測試寫法可以參考 Day 20Day 23

今天已經介紹了有關於 Ohara 單元測試的部份了,明天會再繼續說明有關於整合測試的部份。


上一篇
Day 24 部署自定義的 Sink Connector
下一篇
Day 26 關於 Ohara 的測試程式 (二)
系列文
用30天介紹 open source 專案 Ohara 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言